import { Request, Response } from 'express';
import jwt, { SignOptions } from 'jsonwebtoken';
import { User, IUser } from '../models/User';
import { logger } from '../utils/logger';
import config from '../config/env';
import { createError } from '../middleware/errorHandler';
import { generateResetToken, verifyResetToken, generateResetLink } from '../utils/resetToken';
import { sendEmail, generateResetPasswordTemplate } from '../utils/email';
import { checkPasswordStrength, isPasswordReused } from '../utils/password';

export class AuthController {
  // 生成JWT令牌
  private generateTokens(userId: string) {
    const secret = config.JWT_SECRET;
    
    const accessToken = jwt.sign(
      { userId, type: 'access' },
      secret,
      { expiresIn: 15 * 60 } // 15分钟，用秒数
    );
    
    const refreshToken = jwt.sign(
      { userId, type: 'refresh' },
      secret,
      { expiresIn: 7 * 24 * 60 * 60 } // 7天，用秒数
    );
    
    return { accessToken, refreshToken };
  }

  // 用户注册
  public register = async (req: Request, res: Response): Promise<void> => {
    try {
      const { email, password, name, role, bio } = req.body;

      logger.info('用户注册请求', { email, name, role });

      // 检查用户是否已存在
      const existingUser = await User.findOne({ email });
      if (existingUser) {
        logger.warn('注册失败: 邮箱已存在', { email });
        res.status(409).json({
          success: false,
          message: '该邮箱已被注册',
          code: 'EMAIL_EXISTS'
        });
        return;
      }

      // 创建新用户
      const user = new User({
        email: email.toLowerCase(),
        password,
        name: name.trim(),
        role: role || 'teacher',
        bio: bio || ''
      });

      await user.save();

      logger.info('用户注册成功', { 
        userId: user._id, 
        email: user.email, 
        role: user.role 
      });

      // 生成令牌
      const { accessToken, refreshToken } = this.generateTokens((user._id as any).toString());

      res.status(201).json({
        success: true,
        message: '注册成功',
        data: {
          user: {
            id: user._id,
            email: user.email,
            name: user.name,
            role: user.role,
            avatar: user.avatar,
            bio: user.bio,
            createdAt: user.createdAt
          },
          accessToken,
          refreshToken
        }
      });
    } catch (error: any) {
      logger.error('用户注册失败', { error: error.message, stack: error.stack });
      
      // 处理MongoDB重复键错误
      if (error.code === 11000) {
        res.status(409).json({
          success: false,
          message: '该邮箱已被注册',
          code: 'EMAIL_EXISTS'
        });
        return;
      }

      res.status(500).json({
        success: false,
        message: '注册失败，请稍后重试',
        code: 'INTERNAL_ERROR'
      });
    }
  };

  // 用户登录
  public login = async (req: Request, res: Response): Promise<void> => {
    try {
      const { email, password } = req.body;

      logger.info('用户登录请求', { email });

      // 查找用户
      const user = await User.findOne({ email: email.toLowerCase() });
      if (!user) {
        logger.warn('登录失败: 用户不存在', { email });
        res.status(401).json({
          success: false,
          message: '邮箱或密码错误',
          code: 'INVALID_CREDENTIALS'
        });
        return;
      }

      // 检查账户是否被锁定
      if (user.isLocked) {
        logger.warn('登录失败: 账户被锁定', { email, userId: user._id });
        res.status(423).json({
          success: false,
          message: '账户已被锁定，请稍后再试',
          code: 'ACCOUNT_LOCKED'
        });
        return;
      }

      // 验证密码
      const isPasswordValid = await user.comparePassword(password);
      if (!isPasswordValid) {
        logger.warn('登录失败: 密码错误', { email, userId: user._id });
        
        // 增加登录失败次数
        await (user as any).incLoginAttempts();
        
        res.status(401).json({
          success: false,
          message: '邮箱或密码错误',
          code: 'INVALID_CREDENTIALS'
        });
        return;
      }

      // 登录成功，重置失败次数
      if (user.loginAttempts && user.loginAttempts > 0) {
        await (user as any).resetLoginAttempts();
      }

      logger.info('用户登录成功', { 
        userId: user._id, 
        email: user.email, 
        role: user.role 
      });

      // 生成令牌
      const { accessToken, refreshToken } = this.generateTokens((user._id as any).toString());

      res.json({
        success: true,
        message: '登录成功',
        data: {
          user: {
            id: user._id,
            email: user.email,
            name: user.name,
            role: user.role,
            avatar: user.avatar,
            bio: user.bio,
            createdAt: user.createdAt,
            updatedAt: user.updatedAt
          },
          accessToken,
          refreshToken
        }
      });
    } catch (error: any) {
      logger.error('用户登录失败', { error: error.message, stack: error.stack });
      res.status(500).json({
        success: false,
        message: '登录失败，请稍后重试',
        code: 'INTERNAL_ERROR'
      });
    }
  };

  // 刷新令牌
  public refreshToken = async (req: Request, res: Response): Promise<void> => {
    try {
      const { refreshToken } = req.body;

      logger.info('令牌刷新请求');

      // 验证刷新令牌
      const decoded = jwt.verify(refreshToken, config.JWT_SECRET) as { 
        userId: string; 
        type: string; 
      };

      // 检查令牌类型
      if (decoded.type !== 'refresh') {
        logger.warn('令牌刷新失败: 令牌类型错误', { type: decoded.type });
        res.status(401).json({
          success: false,
          message: '无效的刷新令牌',
          code: 'INVALID_TOKEN_TYPE'
        });
        return;
      }

      // 检查用户是否存在
      const user = await User.findById(decoded.userId);
      if (!user) {
        logger.warn('令牌刷新失败: 用户不存在', { userId: decoded.userId });
        res.status(401).json({
          success: false,
          message: '无效的刷新令牌',
          code: 'USER_NOT_FOUND'
        });
        return;
      }

      logger.info('令牌刷新成功', { 
        userId: user._id, 
        email: user.email 
      });

      // 生成新的令牌
      const tokens = this.generateTokens((user._id as any).toString());

      res.json({
        success: true,
        message: '令牌刷新成功',
        data: {
          ...tokens,
          user: {
            id: user._id,
            email: user.email,
            name: user.name,
            role: user.role,
            avatar: user.avatar,
            bio: user.bio
          }
        }
      });
    } catch (error: any) {
      logger.error('令牌刷新失败', { error: error.message });
      
      if (error.name === 'JsonWebTokenError') {
        res.status(401).json({
          success: false,
          message: '无效的刷新令牌',
          code: 'INVALID_TOKEN'
        });
      } else if (error.name === 'TokenExpiredError') {
        res.status(401).json({
          success: false,
          message: '刷新令牌已过期',
          code: 'TOKEN_EXPIRED'
        });
      } else {
        res.status(500).json({
          success: false,
          message: '令牌刷新失败，请重新登录',
          code: 'INTERNAL_ERROR'
        });
      }
    }
  };

  // 登出
  public logout = async (req: Request, res: Response): Promise<void> => {
    try {
      // 在实际应用中，这里可以将令牌加入黑名单
      res.json({
        success: true,
        message: '登出成功'
      });
    } catch (error) {
      console.error('登出错误:', error);
      res.status(500).json({
        success: false,
        message: '服务器内部错误'
      });
    }
  };

  // 忘记密码
  public forgotPassword = async (req: Request, res: Response): Promise<void> => {
    try {
      const { email } = req.body;

      logger.info('忘记密码请求', { email });

      // 查找用户
      const user = await User.findOne({ email: email.toLowerCase() });
      
      // 无论用户是否存在，都返回相同的响应（安全考虑）
      const responseMessage = '如果该邮箱已注册，重置密码邮件已发送';
      
      if (!user) {
        logger.warn('忘记密码: 用户不存在', { email });
        res.json({
          success: true,
          message: responseMessage
        });
        return;
      }

      // 检查用户账户是否被锁定
      if (user.isLocked) {
        logger.warn('忘记密码: 账户被锁定', { email, userId: user._id });
        res.json({
          success: true,
          message: responseMessage
        });
        return;
      }

      // 生成重置令牌
      const resetToken = generateResetToken((user._id as any).toString(), user.email);
      const resetLink = generateResetLink(resetToken);

      // 保存重置令牌到数据库
      user.resetPasswordToken = resetToken;
      user.resetPasswordExpires = new Date(Date.now() + 6 * 60 * 60 * 1000); // 6小时后过期
      await user.save();

      // 发送重置邮件
      const emailTemplate = generateResetPasswordTemplate(user.name, resetLink, 6);
      const emailSent = await sendEmail(user.email, emailTemplate);

      if (emailSent) {
        logger.info('密码重置邮件发送成功', { 
          userId: user._id, 
          email: user.email 
        });
      } else {
        logger.error('密码重置邮件发送失败', { 
          userId: user._id, 
          email: user.email 
        });
      }

      res.json({
        success: true,
        message: responseMessage
      });
    } catch (error: any) {
      logger.error('忘记密码处理失败', { error: error.message, stack: error.stack });
      res.status(500).json({
        success: false,
        message: '请求处理失败，请稍后重试',
        code: 'INTERNAL_ERROR'
      });
    }
  };

  // 重置密码
  public resetPassword = async (req: Request, res: Response): Promise<void> => {
    try {
      const { token, newPassword } = req.body;

      logger.info('密码重置请求');

      // 验证重置令牌
      const decoded = verifyResetToken(token);
      if (!decoded) {
        logger.warn('密码重置失败: 无效令牌');
        res.status(400).json({
          success: false,
          message: '无效或已过期的重置令牌',
          code: 'INVALID_TOKEN'
        });
        return;
      }

      // 查找用户
      const user = await User.findById(decoded.userId);
      if (!user) {
        logger.warn('密码重置失败: 用户不存在', { userId: decoded.userId });
        res.status(400).json({
          success: false,
          message: '无效或已过期的重置令牌',
          code: 'USER_NOT_FOUND'
        });
        return;
      }

      // 检查数据库中的重置令牌
      if (!user.resetPasswordToken || user.resetPasswordToken !== token) {
        logger.warn('密码重置失败: 令牌不匹配', { userId: user._id });
        res.status(400).json({
          success: false,
          message: '无效或已过期的重置令牌',
          code: 'TOKEN_MISMATCH'
        });
        return;
      }

      // 检查令牌是否过期
      if (!user.resetPasswordExpires || user.resetPasswordExpires < new Date()) {
        logger.warn('密码重置失败: 令牌已过期', { userId: user._id });
        res.status(400).json({
          success: false,
          message: '重置令牌已过期，请重新申请',
          code: 'TOKEN_EXPIRED'
        });
        return;
      }

      // 检查新密码强度
      const strengthCheck = checkPasswordStrength(newPassword);
      if (!strengthCheck.isValid) {
        logger.warn('密码重置失败: 密码强度不足', { userId: user._id });
        res.status(400).json({
          success: false,
          message: '密码强度不足',
          code: 'WEAK_PASSWORD',
          errors: strengthCheck.feedback
        });
        return;
      }

      // 检查是否重复使用旧密码
      if (user.passwordHistory && user.passwordHistory.length > 0) {
        const isReused = await isPasswordReused((user._id as any).toString(), newPassword, user.passwordHistory);
        if (isReused) {
          logger.warn('密码重置失败: 密码重复使用', { userId: user._id });
          res.status(400).json({
            success: false,
            message: '不能使用最近使用过的密码',
            code: 'PASSWORD_REUSED'
          });
          return;
        }
      }

      // 更新密码
      user.password = newPassword;
      user.resetPasswordToken = undefined;
      user.resetPasswordExpires = undefined;
      user.lastPasswordChange = new Date();
      
      // 重置登录失败次数
      if (user.loginAttempts && user.loginAttempts > 0) {
        await (user as any).resetLoginAttempts();
      }

      await user.save();

      logger.info('密码重置成功', { 
        userId: user._id, 
        email: user.email 
      });

      res.json({
        success: true,
        message: '密码重置成功，请使用新密码登录'
      });
    } catch (error: any) {
      logger.error('密码重置失败', { error: error.message, stack: error.stack });
      res.status(500).json({
        success: false,
        message: '密码重置失败，请稍后重试',
        code: 'INTERNAL_ERROR'
      });
    }
  };
}